package org.luosl.webmagicx.urlcreator

import org.luosl.webmagicx.conf.PropType.strType
import org.luosl.webmagicx.conf.{ConfException, XmlProps, SpiderConf}
import org.luosl.webmagicx.conf.MatcherConverters._
import play.api.libs.json.Json
import us.codecraft.webmagic.model.HttpRequestBody
import us.codecraft.webmagic.model.HttpRequestBody.ContentType
import us.codecraft.webmagic.utils.HttpConstant._
import us.codecraft.webmagic.{Request, Task}

abstract class AbstractUrlCreator(sc:SpiderConf, task:Task, props:XmlProps) {

  /** 标签 */
  protected val tag:String = props.tag

  /** url */
  protected val url:String = {
    List(
      props.valueOption("text")(strType),
      props.valueOption("value", "text")(strType)
    ).filter(urlOpt=> urlOpt.isDefined && urlOpt.get != "").map(_.get)
      .lastOption.getOrElse(throw ConfException(s"$tag 必须配置 url"))
  }

  /** 请求方式 */
  protected val method:String = props.valueOrDefault("method")(strType)("GET")

  /** 参数 */
  protected val params:Map[String, String] = props.props("params", "param")
    .map(prop=> prop.value("key")(strType) -> prop.value("value")(strType)).toMap

  /**
    * 请求参数类型，当前支持一下三种
    * json
    * xml (待支持)
    * form
    */
  protected val contentType:String = props.valueOrDefault("params", "contentType")(strType)("form")
  /**
    * 请求参数的字符集
    */
  protected val charset:String = props.valueOrDefault("params", "charset")(strType)(sc.attribute.charset)

  /**
    * 获取带参数的 Requests
    * @return
    */
  def requestsWithParam():Seq[Request] = requests().map(req=>withParam(req, params))

  /**
    * 为 request 添加参数信息
    * @param request request
    * @return
    */
  def withParam(request: Request,params:Map[String,String]): Request = {
    val url:String = request.getUrl
    method match {
      case Method.GET =>
        val flag:String = if(url.contains("?")) "&" else "?"
        new Request(request).setUrl(s"$url$flag${formParamStr(params)}")
      case Method.POST =>
        val (bodyStr, types):(String, String) = contentType match {
          case "json" => Json.toJson(params).toString() -> ContentType.JSON
          case "form" => formParamStr(params) -> ContentType.FORM
          case "xml" => throw new RuntimeException("当前版本不支持的 contentType:xml")
          case str:String => throw new RuntimeException(s"当前版本不支持的 contentType:$str")
        }
        val requestBody:HttpRequestBody = new HttpRequestBody(bodyStr.getBytes(charset), types, charset)
        new Request(request).setRequestBody(requestBody)
      case other:String => throw ConfException(s"无效的请求方式:$other")
    }
  }

  /**
    * 获取表单形式的参数字符串
    * @return
    */
  def formParamStr(params:Map[String,String]):String = params.map(kv=> s"${kv._1}=${kv._2}").mkString("&")

  def requests():Seq[Request]

}
